﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;

namespace DimensionsHelper.SourceGeneration.CommandLine
{

	public partial class ArgumentSourceGenerator
	{

		private sealed class Parser
		{

			public Parser(Compilation compilation)
			{
				_compilation = compilation;
			}

			public List<DiagnosticInfo> Diagnostics { get; } = new List<DiagnosticInfo>();
			private Location _contextClassLocation;
			private readonly Compilation _compilation;


			public const string ArgumentContextFullName = "DimensionsHelper.Serialization.CommandLine.ArgumentContext";
			public const string ArgumentContextAttributeFullName = "DimensionsHelper.Serialization.CommandLine.ArgumentContextAttribute";
			public const string ArgumentObjectAttributeFullName = "DimensionsHelper.Serialization.CommandLine.ArgumentObjectAttribute";
			public const string ArgumentPropertyAttributeFullName = "DimensionsHelper.Serialization.CommandLine.ArgumentPropertyAttribute";


			private INamedTypeSymbol _argumentContextType;
			private INamedTypeSymbol ArgumentContextType
			{
				get
				{
					return _argumentContextType ?? (_argumentContextType = _compilation.GetBestTypeByMetadataName(ArgumentContextFullName));
				}
			}


			private INamedTypeSymbol _argumentObjectAttributeType;
			private INamedTypeSymbol ArgumentObjectAttributeType
			{
				get
				{
					return _argumentObjectAttributeType ?? (_argumentObjectAttributeType = _compilation.GetBestTypeByMetadataName(ArgumentObjectAttributeFullName));
				}
			}


			private INamedTypeSymbol _argumentObjectPropertyAttributeType;
			private INamedTypeSymbol ArgumentObjectPropertyAttributeType
			{
				get
				{
					return _argumentObjectPropertyAttributeType ?? (_argumentObjectPropertyAttributeType = _compilation.GetBestTypeByMetadataName(ArgumentPropertyAttributeFullName));
				}
			}

			public void ReportDiagnostic(DiagnosticDescriptor descriptor, Location location, params object[] messageArgs)
			{
				Debug.Assert(_contextClassLocation != null);

				if (location is null || !_compilation.ContainsLocation(location))
				{
					location = _contextClassLocation;
				}

				Diagnostics.Add(DiagnosticInfo.Create(descriptor, location, messageArgs));
			}


			public GenerationInfo ParseContextGenerationInfo(ClassDeclarationSyntax classDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken)
			{
				if (ArgumentContextType == null)
				{
					return null;
				}

				INamedTypeSymbol contextSymbol = semanticModel.GetDeclaredSymbol(classDeclaration, cancellationToken);

				if (contextSymbol == null)
				{
					return null;
				}

				_contextClassLocation = contextSymbol.GetLocation();
				Debug.Assert(_contextClassLocation != null);

				if (!_argumentContextType.IsAssignableFrom(contextSymbol))
				{
					ReportDiagnostic(CommandLineDiagnostics.ArgumentContextAttributeOnNonContextType, _contextClassLocation, contextSymbol.ToDisplayString());
					return null;
				}

				var objectList = new List<ObjectGenerationInfo>();
				var typesToGen = ProcessTypeToGenerate(contextSymbol);

				foreach (var obj in typesToGen)
				{
					var objToGen = ProcessObjectGenerationInfo(obj);

					if (objToGen != null)
					{
						objectList.Add(objToGen);
					}
				}

				return new GenerationInfo(
					contextSymbol.ContainingNamespace.ToDisplayString(),
					contextSymbol.Name,
					objectList.ToArray(),
					contextSymbol.DeclaredAccessibility == Accessibility.Internal);
			}


			private class TypeToGenerate
			{
				public TypeToGenerate(string contextNamespace, string contextClassName, ITypeSymbol type, Location location, Location attributeLocation, bool isInternal)
				{
					ContextNamespace = contextNamespace;
					ContextClassName = contextClassName;
					Type = type;
					Location = location;
					AttributeLocation = attributeLocation;
					IsInternal = isInternal;
				}

				public string ContextNamespace { get; }
				public string ContextClassName { get; }
				public bool IsInternal { get; }
				public ITypeSymbol Type { get; }
				public Location Location { get; }
				public Location AttributeLocation { get; }
			}


			private TypeToGenerate ProcessArgumentObjectAttribute(AttributeData attributeData, string contextNamespace, string contextClassName, bool isInternal)
			{
				if (attributeData.ConstructorArguments.Length != 1)
				{
					return null;
				}

                if (!(attributeData.ConstructorArguments[0].Value is ITypeSymbol typeSymbol))
                {
                    return null;
                }

                Location location = typeSymbol.GetLocation();
				Location attrLocation = attributeData.GetLocation();

				if (location == null || !_compilation.ContainsLocation(location))
				{
					location = attrLocation;
				}

				return new TypeToGenerate(
					contextNamespace,
					contextClassName, 
					typeSymbol, 
					location, 
					attrLocation,
					isInternal);
			}


			private List<TypeToGenerate> ProcessTypeToGenerate(INamedTypeSymbol contextClassSymbol)
			{
				var res = new List<TypeToGenerate>();

				foreach (var attr in contextClassSymbol.GetAttributes())
				{
					var attrSymbol = attr.AttributeClass;

					if (SymbolEqualityComparer.Default.Equals(attrSymbol, ArgumentObjectAttributeType))
					{
						var typeToGen = ProcessArgumentObjectAttribute(
							attr, 
							contextClassSymbol.ContainingNamespace.ToDisplayString(), 
							contextClassSymbol.Name,
							contextClassSymbol.DeclaredAccessibility == Accessibility.Internal);
						if (typeToGen != null)
						{
							res.Add(typeToGen);
						}
					}
				}

				return res;
			}


			private class AttributeInfo
			{ 
				public AttributeInfo(ObjectPropertyGenerationInfo data, Location location)
				{
					Data = data;
					Location = location;
				}

				public ObjectPropertyGenerationInfo Data { get; }
				public Location Location { get; }
			}


			private bool GetPropertyTypeName(
				IPropertySymbol propertySymbol,
				Location reportLocation,
				out string typeName,
				out bool isNullable,
				out bool isListInterface,
				out bool isList,
				out ITypeSymbol listElementTypeName,
				out bool isDictionaryInterface,
				out bool isDictionary,
				out string dictionaryKeyTypeName,
				out string dictionaryValueTypeName)
			{
				typeName = propertySymbol.Type.GetSymbolFullName();
				isNullable = false;
				isListInterface = false;
				isList = false;
				isDictionaryInterface = false;
				isDictionary = false;
				listElementTypeName = null;
				dictionaryKeyTypeName = null;
				dictionaryValueTypeName = null;

				if (propertySymbol.Type is INamedTypeSymbol namedTypeSymbol &&
					namedTypeSymbol.IsGenericType &&
					namedTypeSymbol.TypeArguments.Length > 0)
				{
					int parametersCount = namedTypeSymbol.TypeArguments.Length;
					string displayName = namedTypeSymbol.GetSymbolFullName();
					var firstArg = namedTypeSymbol.TypeArguments[0];

					// generic
					var ltIndex = displayName.IndexOf('<');
					if (ltIndex > 0)
					{
						displayName = displayName.Substring(0, ltIndex);
					}

					string firstArgTypeName = firstArg.GetSymbolFullName();
					int needParamsLength;
					switch (displayName)
					{
						case KnownSymbolHelper.GlobalSystemNullableTypeFullName:
							needParamsLength = 1;
							isNullable = true;
							typeName = firstArgTypeName;
							break;

						case KnownSymbolHelper.GlobalSystemListInterfaceTypeFullName:
							needParamsLength = 1;
							isListInterface = true;
							listElementTypeName = firstArg;
							break;

						case KnownSymbolHelper.GlobalSystemListTypeFullName:
							needParamsLength = 1;
							isList = true;
							listElementTypeName = firstArg;
							break;

						case KnownSymbolHelper.GlobalSystemDictionaryInterfaceTypeFullName:
							needParamsLength = 2;
							isDictionaryInterface = true;
							dictionaryKeyTypeName = firstArgTypeName;
							if (parametersCount == 2)
							{
								dictionaryValueTypeName = namedTypeSymbol.TypeArguments[1].GetSymbolFullName();
							}
							break;

						case KnownSymbolHelper.GlobalSystemDictionaryTypeFullName:
							needParamsLength = 2;
							isDictionary = true;
							dictionaryKeyTypeName = firstArgTypeName;
							if (parametersCount == 2)
							{
								dictionaryValueTypeName = namedTypeSymbol.TypeArguments[1].GetSymbolFullName();
							}
							break;

						default:
							ReportDiagnostic(CommandLineDiagnostics.UnsupportedGenericType, reportLocation);
							return false;
					}

					if (needParamsLength != parametersCount)
					{
						return false;
					}

				}

				return true;
			}


			private class DictionaryPropertyPair
			{				
				public string DictionaryOriginalType { get; set; }

				public AttributeInfo Key { get; set; }

				public AttributeInfo Value { get; set; }
			}


			private static void AddToRegularPropertyBindingCache(List<AttributeInfo> regulars, AttributeInfo property)
			{
				regulars.Add(property);

				if (!string.IsNullOrEmpty(property.Data.PropertyBinding))
				{
					property.Data.PropertyName = property.Data.PropertyBinding;
					property.Data.PropertyBinding = null;
				}
			}


			private void TrySetToDictionaryPropertyBindingCache(Dictionary<string, DictionaryPropertyPair> cache, AttributeInfo info, string dictTypeName, bool isKey)
			{
				if (string.IsNullOrEmpty(info.Data.PropertyBinding))
				{
					return;
				}

                if (!cache.TryGetValue(info.Data.PropertyBinding, out var pair))
                {
                    pair = new DictionaryPropertyPair 
					{ 
						DictionaryOriginalType = dictTypeName 
					};

                    cache.Add(info.Data.PropertyBinding, pair);
                }

                if (isKey)
                {
                    if (pair.Key != null)
                    {
                        ReportDiagnostic(CommandLineDiagnostics.PropertyHasBoundToDictionaryKey, info.Location);
                    }
                    else
                    {
                        pair.Key = info;
                    }
                }
                else
                {
                    if (pair.Value != null)
                    {
                        ReportDiagnostic(CommandLineDiagnostics.PropertyHasBoundToDictionaryValue, info.Location);
                    }
                    else
                    {
                        pair.Value = info;
                    }
                }

				info.Data.PropertyName = info.Data.PropertyBinding;
				info.Data.PropertyBinding = null;
            }


			private bool CheckBasicType(string typeDisplayName, Location location)
			{
				if (!KnownSymbolHelper.IsBasicValueTypeDisplayName(typeDisplayName))
				{
					ReportDiagnostic(CommandLineDiagnostics.UnsupportedGenericType, location);
					return false;
				}

				return true;
			}


            private bool ProcessObjectPropertyCustomAttributes(
				HashSet<string> existArgumentNames,
				string propertyClassName,
				string propertyClassFullName,
				string propertyOriginalTypeFullName,
				ITypeSymbol listElementSymbol,
				IPropertySymbol propertySymbol,
				bool isBasicType,
				bool isList,
				bool isDictionary,
				string dictionaryKeyTypeName,
				string dictionaryValueTypeName,
				out AttributeInfo propertyAttribute,
				out AttributeInfo dictionaryKey,
				out AttributeInfo dictionaryValue,
				out AttributeInfo[] objectProperties,
				out ObjectDictionaryPropertyGenerationInfo[] objectDictionaryProperties)
			{

				propertyAttribute = null;
				objectProperties = null;
				objectDictionaryProperties = null;
				dictionaryKey = null;
				dictionaryValue = null;

				var objectPropertiesList = new List<AttributeInfo>();
				var objectDictionaryPropertiesDict = new Dictionary<string, DictionaryPropertyPair>();

				foreach (var attributeData in propertySymbol.GetAttributes())
				{
					bool isPropertyBasicType = isBasicType;
					bool isPropertyList = isList;
					bool isPropertyDict = isDictionary;
					bool isObjectType = !isBasicType && !isList && !isDictionary;
					bool isReadOnly = propertySymbol.IsReadOnly;
					bool isWriteOnly = propertySymbol.IsWriteOnly;

					string className = propertyClassName;
					string classFullName = propertyClassFullName;
					string propName = propertySymbol.Name;
					string propTypeName = propertySymbol.Type.ToDisplayString();
					string propOriginalTypeFullName = propertyOriginalTypeFullName;
					string propImplTypeFullName = KnownSymbolHelper.GetImplementTypeName(propertyOriginalTypeFullName);

					var propListElementSymbol = listElementSymbol;
					var attributeType = attributeData.AttributeClass;

					if (attributeType == null)
					{
						continue;
					}

					var displayName = attributeType.ToDisplayString();

					if (displayName != ArgumentPropertyAttributeFullName)
					{
						continue;
					}

					// ArgumentPropertyAttribute(string argName)
					// Properties
					//	1. ArgumentName
					//	2. IsDictionaryKey
					//  3. IsDictionaryValue
					//  4. Separator

					string argName = null;
					bool isDictKey = false;
					bool isDictValue = false;
					string listSep = null;
					string propertyBinding = null;
					bool canFollow = false;

					if (attributeData.ConstructorArguments.Length == 1)
					{
						argName = attributeData.ConstructorArguments[0].Value as string;
					}

					foreach (var namedArg in attributeData.NamedArguments)
					{
						switch (namedArg.Key)
						{
							case "ArgumentName":
								argName = namedArg.Value.Value as string;
								break;

							case "IsDictionaryKey":
								if (namedArg.Value.Value is bool blnIsDictKey)
								{
									isDictKey = blnIsDictKey;
								}
								break;

							case "IsDictionaryValue":
								if (namedArg.Value.Value is bool blnIsDictValue)
								{
									isDictValue = blnIsDictValue;
								}
								break;

							case "Separator":
								listSep = namedArg.Value.Value as string;
								break;

							case "PropertyBinding":
								propertyBinding = namedArg.Value.Value as string;
								break;

							case "CanFollow":
								if (namedArg.Value.Value is bool blnCanFollow)
								{
									canFollow = blnCanFollow;
								}
								break;

                            default:
								break;
						}
					}

					var loc = attributeData.GetLocation();

					// 参数名不允许重复
					if (!string.IsNullOrEmpty(argName))
					{
						if (!existArgumentNames.Add(argName))
						{
							ReportDiagnostic(CommandLineDiagnostics.DuplicateArgumentName, loc);
							continue;
						}
					}

					bool isBindingProperty = !string.IsNullOrEmpty(propertyBinding);
					ITypeSymbol type = null;

					// 绑定属性只支持基础类型、列表和字典
					// 同时，修正类型名
					if (isBindingProperty)
					{
						ITypeSymbol targetPropertyTypeSymbol = propListElementSymbol ?? propertySymbol.Type;
						
						// 如果类型是接口，报告错误并继续
						if (targetPropertyTypeSymbol.TypeKind == TypeKind.Interface)
						{
							ReportDiagnostic(CommandLineDiagnostics.PropertyCantBindToInterface, loc);
							continue;
						}

                        // 如果属性不存在，报告错误并继续
                        if (!KnownSymbolHelper.TryGetTypePropertySymbol(targetPropertyTypeSymbol, propertyBinding, out var targetProperty, out var targetField))
						{
                            ReportDiagnostic(CommandLineDiagnostics.BindingPropertyIsNotExist, loc);
							continue;
                        }

						// 此时对应的是属性值类型的属性
						type = targetProperty?.Type ?? targetField.Type;
                        className = targetPropertyTypeSymbol.Name;
                        classFullName = targetPropertyTypeSymbol.GetSymbolFullName();
                        propTypeName = type.Name;
                        propOriginalTypeFullName = type.GetSymbolFullName();
                        propImplTypeFullName = KnownSymbolHelper.GetImplementTypeName(propOriginalTypeFullName);
                        isPropertyBasicType = KnownSymbolHelper.IsBasicValueTypeDisplayName(propOriginalTypeFullName) || type.TypeKind == TypeKind.Enum;
                        isPropertyList = KnownSymbolHelper.IsListDisplayName(propOriginalTypeFullName);
                        isReadOnly = targetProperty?.IsReadOnly ?? targetField.IsReadOnly;
                        isWriteOnly = targetProperty?.IsWriteOnly ?? false;

                        // List
                        if (KnownSymbolHelper.TryGetListGenericArgument(type, out var listGenericSymbol))
						{
							if (!CheckBasicType(listGenericSymbol.GetSymbolFullName(), loc))
							{
								continue;
							}

							propListElementSymbol = listGenericSymbol;
							isPropertyBasicType = false;
							isPropertyList = true;
							isObjectType = false;
						}
						// Dictionary
						else if (KnownSymbolHelper.TryGetDictionaryGenericArguments(type, out var keySymbol, out var valueSymbol))
						{
							var dictGenericSymbol = isDictKey ? keySymbol : valueSymbol;
							propTypeName = dictGenericSymbol.Name;
							propOriginalTypeFullName = dictGenericSymbol.GetSymbolFullName();
							propImplTypeFullName = KnownSymbolHelper.GetImplementTypeName(propOriginalTypeFullName);

							if (!CheckBasicType(propOriginalTypeFullName, loc))
							{
								continue;
							}

							isPropertyBasicType = true;
							isPropertyList = false;
							isObjectType = false;
						}
					}

					// 字典关键字重复
					if (!isBindingProperty && dictionaryKey != null && isDictKey)
					{
						ReportDiagnostic(CommandLineDiagnostics.DuplicateDictionaryKey, loc);
						continue;
					}

					// 字典值重复
					if (!isBindingProperty && dictionaryValue != null && isDictValue)
					{
						ReportDiagnostic(CommandLineDiagnostics.DuplicateDictionaryValue, loc);
						continue;
					}

					// 字典注解需要属性名是字典类型
					if (!isBindingProperty && !isPropertyDict && (isDictKey || isDictValue))
					{
						ReportDiagnostic(CommandLineDiagnostics.PropertyTypeIsNotDictionary, loc);
						continue;
					}

					// 字典类型的Key和Value只支持基础类型
					// 同时修正字典关键字和值类型
					if (!isBindingProperty && isPropertyDict && !string.IsNullOrEmpty(dictionaryKeyTypeName))
					{
						isPropertyBasicType = KnownSymbolHelper.IsBasicValueTypeDisplayName(dictionaryKeyTypeName);
						if (!isPropertyBasicType)
						{
							ReportDiagnostic(CommandLineDiagnostics.UnsupportedDictionaryKeyOrValueType, loc);
							continue;
						}

						propOriginalTypeFullName = dictionaryKeyTypeName;
						propImplTypeFullName = dictionaryKeyTypeName;
                    }

					if (!isBindingProperty && isDictValue && !string.IsNullOrEmpty(dictionaryValueTypeName))
					{
                        isPropertyBasicType = KnownSymbolHelper.IsBasicValueTypeDisplayName(dictionaryValueTypeName);
						if (!isPropertyBasicType)
						{
							ReportDiagnostic(CommandLineDiagnostics.UnsupportedDictionaryKeyOrValueType, loc);
							continue;
						}
						
						propOriginalTypeFullName = dictionaryValueTypeName;
						propImplTypeFullName = dictionaryValueTypeName;
					}

					var generationInfo = new ObjectPropertyGenerationInfo
					{
						ClassName = className,
						ClassFullName = classFullName,
						PropertyName = propName,
						ArgumentName = argName,
						ListSeparator = listSep,
						TypeName = propTypeName,
						OriginalTypeFullName = propOriginalTypeFullName,
						ImplTypeFullName = propImplTypeFullName,
						IsBasicType = isPropertyBasicType,
						IsList = isPropertyList,
						IsReadOnly = isReadOnly,
						IsWriteOnly = isWriteOnly,
						IsObjectRef = isObjectType,
						ListElementType = propListElementSymbol.GetSymbolFullName(),
						PropertyBinding = propertyBinding,
						CanFollow = canFollow,
						ObjectProperties = ImmutableArray<ObjectPropertyGenerationInfo>.Empty,
						ObjectDictionaryProperties = ImmutableArray<ObjectDictionaryPropertyGenerationInfo>.Empty
					};

					var attrInfo = new AttributeInfo(generationInfo, loc);

					if (isDictKey)
					{
						if (isBindingProperty)
						{
							TrySetToDictionaryPropertyBindingCache(objectDictionaryPropertiesDict, attrInfo, type.GetSymbolFullName(), true);
						}
						else
						{
                            dictionaryKey = attrInfo;
                        }
                    }
					else if (isDictValue)
					{
						if (isBindingProperty)
						{
							TrySetToDictionaryPropertyBindingCache(objectDictionaryPropertiesDict, attrInfo, type.GetSymbolFullName(), false);
						}
						else
						{
                            dictionaryValue = attrInfo;
                        }
                    }
					// 如果有多个非字典注解，添加到对象注解列表
					else if (propertyAttribute == null && objectPropertiesList.Count == 0)
					{
						propertyAttribute = attrInfo;
					}
					else
					{
						if (propertyAttribute != null)
						{
							AddToRegularPropertyBindingCache(objectPropertiesList, propertyAttribute);
							propertyAttribute = null;
						}

						AddToRegularPropertyBindingCache(objectPropertiesList, attrInfo);
					}
				}

				if (objectPropertiesList.Count > 0)
				{
					objectProperties = objectPropertiesList.ToArray();
				}				

				var effectiveDicts = objectDictionaryPropertiesDict
					.Values
					.Where(pair =>
					{
						if (pair.Key == null && pair.Value == null)
						{
							return false;
						}

						if (pair.Value == null && pair.Key != null)
						{
							ReportDiagnostic(CommandLineDiagnostics.MissingDictionaryValueProperty, pair.Key.Location);
							return false;
						}

						if (pair.Key == null && pair.Value != null)
						{
							ReportDiagnostic(CommandLineDiagnostics.MissingDictionaryKeyProperty, pair.Value.Location);
							return false;
						}

						return true;
					});

				if (effectiveDicts.Any())
				{
					objectDictionaryProperties = effectiveDicts.Select(e =>
					{
						var dictInfo = new ObjectDictionaryPropertyGenerationInfo
						{
							ClassName = propertyClassName,
							ClassFullName = propertyClassFullName,
							PropertyName = propertySymbol.Name,
							DictionaryType = e.DictionaryOriginalType,
							DictionaryValueType = KnownSymbolHelper.GetImplementTypeName(e.DictionaryOriginalType),
							IsReadOnly = propertySymbol.IsReadOnly,
							IsWriteOnly = propertySymbol.IsWriteOnly,
							Key = e.Key.Data,
							Value = e.Value.Data
						};

						e.Key.Data.IsDictionaryKey = true;
						e.Value.Data.IsDictionaryValue = true;

						return dictInfo;
					}).ToArray();
				}

				// 字典属性优先级高于其他属性
				// 同时，需要核对属性类型，只有属性是字典类型时，才报错
				if (dictionaryKey != null || dictionaryValue != null)
				{
					if (dictionaryKey == null)
					{
						ReportDiagnostic(CommandLineDiagnostics.MissingDictionaryValueProperty, dictionaryValue.Location);
						return false;
					}

					if (dictionaryValue == null)
					{
						ReportDiagnostic(CommandLineDiagnostics.MissingDictionaryKeyProperty, dictionaryKey.Location);
						return false;
					}

					if (propertyAttribute != null && isDictionary)
					{
						ReportDiagnostic(CommandLineDiagnostics.PropertyHasDictionaryAttribute, propertyAttribute.Location);
						propertyAttribute = null;
					}

					if (objectProperties != null && isDictionary)
					{
						foreach (var prop in objectProperties)
						{
							ReportDiagnostic(CommandLineDiagnostics.PropertyHasDictionaryAttribute, prop.Location);
						}

						objectProperties = null;
					}
				}

				return true;
			}


			private bool ProcessPropertyGenerationInfo(
				HashSet<string> existArgName,
				string className,
				string classFullName,
				IPropertySymbol propertySymbol,
				out ObjectPropertyGenerationInfo propertyGenerationInfo,
				out ObjectDictionaryPropertyGenerationInfo objectDictionaryPropertyGenerationInfo)
			{
				propertyGenerationInfo = null;
				objectDictionaryPropertyGenerationInfo = null;

				var attrs = propertySymbol.GetAttributes().Where(attr => attr.AttributeClass.IsAssignableFrom(ArgumentObjectPropertyAttributeType));
				var first = attrs.FirstOrDefault();

				if (first == null ||
					!GetPropertyTypeName(
						propertySymbol,
						first.GetLocation(),
						out string typeName,
						out bool isNullable,
						out bool isListInterface,
						out bool isList,
						out ITypeSymbol listElementTypeSymbol,
						out bool isDictionaryInterface,
						out bool isDictionary,
						out string dictionaryKeyTypeName,
						out string dictionaryValueTypeName))
				{
					return false;
				}

				bool isBasicType = KnownSymbolHelper.IsBasicValueTypeDisplayName(typeName) || 
						propertySymbol.Type.TypeKind == TypeKind.Enum;

				if (!ProcessObjectPropertyCustomAttributes(
					existArgName,
					className,
					classFullName,
					typeName,
					listElementTypeSymbol,
					propertySymbol,
					isBasicType,
					isList || isListInterface,
					isDictionary || isDictionaryInterface,
					dictionaryKeyTypeName,
					dictionaryValueTypeName,
					out var propertyAttribute,
					out var dictionaryKey,
					out var dictionaryValue,
					out var objectProperties,
					out var objectDictionaryProperties))
				{
					return false;
				}

				// 单个属性
				if (propertyAttribute != null)
				{
					propertyGenerationInfo = propertyAttribute.Data;
				}
				// 字典属性
				else if (dictionaryKey != null && dictionaryValue != null)
				{
					objectDictionaryPropertyGenerationInfo = new ObjectDictionaryPropertyGenerationInfo
					{
						ClassName = className,
						ClassFullName = classFullName,
						PropertyName = propertySymbol.Name,
						DictionaryType = typeName,
						DictionaryValueType = KnownSymbolHelper.GetImplementTypeName(typeName),
						IsReadOnly = propertySymbol.IsReadOnly,
						IsWriteOnly = propertySymbol.IsWriteOnly,
						Key = dictionaryKey.Data,
						Value = dictionaryValue.Data,
					};

					dictionaryKey.Data.IsDictionaryKey = true;
					dictionaryValue.Data.IsDictionaryValue = true;
				}
				// 通过多个注解绑定的对象参数
				else if (objectProperties != null || objectDictionaryProperties != null)
				{
					string listElementTypeName = null;
					if (listElementTypeSymbol != null)
					{
						listElementTypeName = listElementTypeSymbol.GetSymbolFullName();
						listElementTypeName = KnownSymbolHelper.GetImplementTypeName(listElementTypeName);
					}

					propertyGenerationInfo = new ObjectPropertyGenerationInfo
					{
						ClassName = className,
						ClassFullName = classFullName,
						PropertyName = propertySymbol.Name,
						ArgumentName = "",
						ListSeparator = null,
						ListElementType = listElementTypeName,
						TypeName = propertySymbol.Type.Name,
						OriginalTypeFullName = typeName,
						ImplTypeFullName = KnownSymbolHelper.GetImplementTypeName(typeName),
						IsBasicType = false,
						IsList = isList,
						IsReadOnly = propertySymbol.IsReadOnly,
						IsWriteOnly = propertySymbol.IsWriteOnly,
						IsObjectRef = true,
						CanFollow = false,
						PropertyBinding = null,
						ObjectProperties = objectProperties != null ? objectProperties.Select(e => e.Data).ToImmutableArray() : ImmutableArray<ObjectPropertyGenerationInfo>.Empty,
						ObjectDictionaryProperties = objectDictionaryProperties != null ? objectDictionaryProperties.ToImmutableArray() : ImmutableArray<ObjectDictionaryPropertyGenerationInfo>.Empty
					};
				}
				else
				{
					return false;
				}

				return true;
			}


			private ObjectGenerationInfo ProcessObjectGenerationInfo(TypeToGenerate typeToGenerate)
			{
				var existArguments = new HashSet<string>();
				var regularProperties = new List<ObjectPropertyGenerationInfo>();
				var dictionaryProperties = new List<ObjectDictionaryPropertyGenerationInfo>();

				var className = typeToGenerate.Type.Name;
				var classFullName = typeToGenerate.Type.GetSymbolFullName();

				foreach (var member in typeToGenerate.Type.GetMembers())
				{
					if (!(member is IPropertySymbol propertySymbol))
					{
						continue;
					}

					var hasAttr = member.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, ArgumentObjectPropertyAttributeType));

					if (!hasAttr)
					{
						continue;
					}

					if (ProcessPropertyGenerationInfo(existArguments,
						className,
                        classFullName,
						propertySymbol,
						out var propertyGenerationInfo,
						out var objectDictionaryPropertyGenerationInfo))
					{
						if (propertyGenerationInfo != null)
						{
							regularProperties.Add(propertyGenerationInfo);
						}

						if (objectDictionaryPropertyGenerationInfo != null)
						{
							dictionaryProperties.Add(objectDictionaryPropertyGenerationInfo);
						}
					}
				}

				if (regularProperties.Count > 0 || dictionaryProperties.Count > 0)
				{
					return new ObjectGenerationInfo
					{
						Namespace = typeToGenerate.ContextNamespace,
						ContextClassName = typeToGenerate.ContextClassName,
						TypeName = className,
						TypeFullName = classFullName,
                        RegularProperties = regularProperties.ToImmutableArray(),
						DictionaryProperties = dictionaryProperties.ToImmutableArray(),
					};
				}

				return null;
			}


		}

	}
}
